
#ifndef __case_http_kenny_kfjiweur89u83r33e3e__
#define __case_http_kenny_kfjiweur89u83r33e3e__

#include <functional>

#include <hude/base/log.hpp>
#include <hude/aio/context.hpp>
#include <hude/aio/http.hpp>

#include "test/test.hpp"

namespace test
{
    //--------------------------------------------------------------------------------
    struct case_http_t : public hude::test::case_t
    {
        //--------------------------------------------------------------------------------
        using http_t = hude::http::http_t< hude::aio::emDEFAULT_MAX_EPOLL >;

        //--------------------------------------------------------------------------------
        int result;

        http_t::aio_t aio;

        http_t::http_listen_t listener;

        http_t::http_connect_t< http_t::evts_cli_htp_t > connector_htp;
        http_t::http_connect_t< http_t::evts_cli_sse_t > connector_sse;
        http_t::http_connect_t< http_t::evts_cli_wsk_t > connector_wsk;

        //--------------------------------------------------------------------------------
        bool run() override
        {
            enum : uint16_t { emPORT = 2002U };

            auto cb_entry = [=]() -> void
            {
                //--------------------------------------------------------------------------------
                auto on_recv = [=]( http_t::user_t& user, http_t::sender_wsk_t sender, const http_t::head_t& h, http_t::buffer_t& b )
                {
                    auto title = http_t::user_cast< std::string >( user );  // 取用户自定义数据

                    std::string str{ b.begin(), b.end() };

                    result -= 1;

                    log_inf( "\t index( %d ) websocket %s on_recv( %u ): %s", result, title.c_str(), b.size(), str.c_str() );

                    b.pop_back(); // 传递少发一个数据

                    if( b.empty() )
                    {
                        log_inf( "\t websocket %s terminate()", title.c_str() );

                        std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );

                        sender->terminate();

                        aio->cancel();
                    } else {
                        // log_inf( "\t websocket %s    send( %u ): %s", title.c_str(), b.size(), str.c_str() );

                        sender->send( 1U, &(b[0]), b.size() );

                        result += 1;
                    }
                };

                //--------------------------------------------------------------------------------
                {
                    http_t::on_reply_t on_reply = []( const http_t::req_t& req, http_t::res_t& res )
                    {
                        std::string say( req.payload_.begin(), req.payload_.end() );

                        auto name = req.value_or_default< std::string >( "name", "body" );

                        log_inf( "\t http server on_reply( req: \"%s %s\" )", say.c_str(), name.c_str() );

                        res( "%s %s!!!", say.c_str(), name.c_str() );
                    };

                    http_t::evts_ser_sse_t ess
                    {
                        []( http_t::user_t& )
                        {
                            log_nte( "SSE server on_close()" );
                        },
                        [=]( const http_t::address_t& source, http_t::user_t& user, http_t::sender_sse_t sender )
                        {
                            log_inf( "SSE server on_session( %s )", source.str().c_str() );

                            // 延时主动推送1条消息
                            aio->task_commit( [=]{
                                sender->send( "hello SSE!!!" );
                            }, 500U );
                        }
                    };

                    http_t::evts_ser_wsk_t esw
                    {
                        []( http_t::user_t& )
                        {
                            log_nte( "websocket server on_close()" );
                        },
                        [=]( const http_t::address_t& source, http_t::user_t& user, http_t::sender_wsk_t sender )
                        {
                            log_inf( "websocket server on_session( %s )", source.str().c_str() );
                            user = std::string{ "server" }; //保存用户自定义数据

                            // 延时主动推送1条消息
                            aio->task_commit( [=]{
                                sender->send( "%d%d%d%d%d%d%d%d%d", 1, 2, 3, 4, 5, 6, 7, 8, 9 );
                                result += 1;
                            }, 500U );
                        },
                        on_recv
                    };

                    http_t::static_resource_t sres{ "/static/", "./hude" };

                    listener.get( "/hello",     on_reply    );
                    listener.get( "/match/*",   on_reply    );
                    listener.get( "/static/*",  sres        );
                    listener.sse( "/sse",       ess         );
                    listener.wsk( "/wsk",       esw         );

                    if( ! listener( aio, "0.0.0.0", emPORT, 100U ) )
                    {
                        log_err( "listen error: %s", listener.error() );
                    }
                }

                //--------------------------------------------------------------------------------
                auto cb_cli_htp = [=]()
                {
                    http_t::evts_cli_htp_t ech
                    {
                        // event on_close
                        []( http_t::user_t& ) {
                            log_nte( "http client on_close()" );
                        },
                        // event on_session
                        [=]( const http_t::address_t& target, http_t::req_t& req ) {
                            log_inf( "http client on_session( %s ), index( %d  )", target.str().c_str(), result );
                            req( "hi" );
                            result += 1;
                        },
                        // event on_handle
                        [=]( const http_t::req_t& req, http_t::res_t& res )
                        {
                            result -= 1;

                            if( res.code_ == http_t::code_t::emOK )
                            {
                                log_inf( "\t index( %d ) http client on_reply( res: \"%s\" )", result,
                                    std::string{ res.payload_.begin(), res.payload_.end() }.c_str() );
                            } else {
                                log_err( "error: request %s", res.path_.c_str() );
                            }
                        }
                    };

                    auto call_htp = [=]( const char* url )
                    {
                        connector_htp( aio, "127.0.0.1", emPORT, ech, url );

                        auto ret = connector_htp.state();

                        if( !ret || ! *ret )
                        {
                            log_err( "http connect error: %s", connector_htp.error() );
                        }
                    };

                    call_htp( "/hello" );

                    for( int i = 0; i < 3; ++i )
                    {
                        std::this_thread::sleep_for( std::chrono::milliseconds( 100U ) );
                        call_htp( hude::format_t{ "/match/c?name=user%d", i }.c_str() );
                    }

                    call_htp( "/static/README.md" );
                };

                //-------------------------------------------------------------------------------
                auto cb_cli_wsk = [=]()
                {
                    http_t::evts_cli_wsk_t ecw
                    {
                        []( http_t::user_t& )
                        {
                            log_nte( "websocket client on_close()" );
                        },
                        []( const http_t::address_t& target, http_t::user_t& user, http_t::sender_wsk_t sender )
                        {
                            log_inf( "websocket client on_session( %s )", target.str().c_str() );
                            user = std::string{ "client" }; //保存用户自定义数据
                        },
                        on_recv
                    };

                    connector_wsk( aio, "127.0.0.1", emPORT, ecw, "/wsk" );

                    auto ret = connector_wsk.state();

                    if( !ret || ! *ret )
                    {
                        log_err( "websocket connect error: %s", connector_wsk.error() );
                    }
                };

                // //--------------------------------------------------------------------------------
                // auto cb_cli_sse = [=]()
                // {
                //     http_t::evts_cli_sse_t ecs
                //     {
                //         []( http_t::user_t& ) {
                //             log_nte( "SSE client on_close()" );
                //         },
                //         [=]( const http_t::address_t& target, http_t::user_t& ) {
                //             log_inf( "SSE client on_session( %s )", target.str().c_str() );
                //             result += 1;
                //         },
                //         [=]( http_t::user_t&, http_t::sender_sse_t sender, http_t::sse_t& sse )
                //         {
                //             result -= 1;

                //             log_inf( R"(     index( %d ) SSE client on_recv( id: "%s", evt: "%s", dat: "%s" ))",
                //                 result, sse.id_.c_str(), sse.event_.c_str(), sse.data_.c_str() );

                //             sender->terminate();
                //         }
                //     };

                //     connector_sse( aio, "127.0.0.1", emPORT, ecs, "/sse" );

                //     auto ret = connector_sse.state();

                //     if( !ret || ! *ret )
                //     {
                //         log_err( "SSE connect error: %s", connector_sse.error() );
                //     }
                // };

                //-------------------------------------------------------------------------------
                aio->task_commit( [=]
                {
                    cb_cli_htp();
                    cb_cli_wsk();
                    // cb_cli_sse();
                }, 500U );
            };

            result = 0;

            aio = http_t::aio();

            // 运行网络上下文的事件泵
            {
                auto ret = aio->run( cb_entry, 500U, false );

                // 退出状态提示
                if( ret < 0 )
                {
                    log_err( "aio error( %d ): %s", ret, aio->error() );
                }
            }

            if( result )
            {
                log_err( "result( %d )", result );
                return false;
            } else {
                return true;
            }
        }
    };

    //--------------------------------------------------------------------------------
    TEST_CASE_ADD( "http", case_http_t, false );

} // namespace test

#endif //__case_http_kenny_kfjiweur89u83r33e3e__


